#include "translate.h"
#include "translate_utils.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int symbolno[MAX_SYMBOL_COUNT];

void translateProgram(Node* program) {
    // printf("\n");
    translateExtdefList(program->ch[0]);
}

void translateExtdefList(Node* extdefList) {
    if (extdefList == NULL) return ;
    translateExtdef(extdefList->ch[0]);
    translateExtdefList(extdefList->ch[1]);
}

void translateExtdef(Node* extDef) {
    // printf("Extdef\n");
    translateSpecifier(extDef->ch[0]); // get specifier
    switch (extDef->ch[1]->elem) {
        case SybExtDecList : 
            translateExtDecList(extDef->ch[1]); 
        case SybSEMI : 
            break;
        case SybFunDec : 
            translateFunDec(extDef->ch[1]); 
            translateCompSt(extDef->ch[2]);
            break;
        default : 
            break;
    }
}

void translateExtDecList(Node* extdecList) {
    // printf("ExtDecList\n");
    translateVarDec(extdecList->ch[0]);
    if (extdecList->cnt > 1) {
        translateExtDecList(extdecList->ch[2]);
        extdecList->snode->nexDec = extdecList->ch[2]->snode;
    }
}

void translateSpecifier(Node* specifier) {
    // printf("Specifier\n");
    switch (specifier->ch[0]->elem) {
        case SybTYPE : 
            Typee(specifier->ch[0], specifier->ch[0]->snode); 
            break;
        case SybStructSpecifier : 
            translateStructSpecifier(specifier->ch[0]); 
            break;
        default : 
            break;
    }
}

void translateStructSpecifier(Node* structSpecifier) {
    // printf("StructSpecifier\n");
    switch (structSpecifier->ch[1]->elem) {
        case SybOptTag :
            translateOptTag(structSpecifier->ch[1]);
            translateDefList(structSpecifier->ch[3]);
            break;
        case SybTag :
            translateTag(structSpecifier->ch[1]);
            break;
        default : 
            break;
    }
}

void translateOptTag(Node* optTag) {
    if (optTag == NULL) return ;
}

void translateTag(Node *tag) {
}

void translateVarDec(Node *varDec) {
    // printf("VarDec\n");
    switch (varDec->cnt) {
        case 1 :
            break;
        case 4 :
            translateVarDec(varDec->ch[0]);
            int array = getVarNo(varDec->snode->name);
            genArrayDeclaration(array, varDec->snode->type->info.array.size);
            break;
        default : 
            break;;
    }
}

void translateFunDec(Node *funDec) {
    genFunctionLabel(funDec->snode->name);
    if (funDec->cnt > 3) {
        translateVarList(funDec->ch[2]);
    }
}

void translateVarList(Node* varList) {
    // printf("VarList\n");
    translateParamDec(varList->ch[0]);
    if (varList->cnt > 1) {
        translateVarList(varList->ch[2]);
    }
}

void translateParamDec(Node* paramDec) {
    translateSpecifier(paramDec->ch[0]);
    translateVarDec(paramDec->ch[1]);

    int param = getVarNo(paramDec->snode->name);
    genFunctionParam(param);
}

void translateCompSt(Node* compSt) {
    translateDefList(compSt->ch[1]);
    translateStmtList(compSt->ch[2]);
}

void translateStmtList(Node* stmtList) {
    if (stmtList == NULL) return ;
    translateStmt(stmtList->ch[0]);
    translateStmtList(stmtList->ch[1]);
}

void translateCondition(Node* exp, int labelTrue, int labelFalse) {
    switch (exp->cnt) {
        case 3 :
            int temp1 = newTemp();
            int temp2 = newTemp();
            if (exp->ch[1]->elem == SybRELOP) {
                translateExp(exp->ch[0], temp1);
                translateExp(exp->ch[2], temp2);
                genIFJump(temp1, temp2, exp->ch[1]->strval, labelTrue, labelFalse);
                genGotoLabel(labelFalse);
            } else if (exp->ch[1]->elem == SybAND) {
                int labelHalfTrue = newLabel();
                translateCondition(exp->ch[0], labelHalfTrue, labelFalse);
                genLabel(labelHalfTrue);
                translateCondition(exp->ch[2], labelTrue, labelFalse);
            } else if (exp->ch[1]->elem == SybOR) {
                int labelHalfFalse = newLabel();
                translateCondition(exp->ch[0], labelTrue, labelHalfFalse);
                genLabel(labelHalfFalse);
                translateCondition(exp->ch[2], labelTrue, labelFalse);
            }
            break;
        case 2 :
            if (exp->ch[0]->elem == SybNOT) {
                translateCondition(exp->ch[1], labelFalse, labelTrue);
            }
            break;
        case 1 :
            int temp = newTemp();
            translateExp(exp, temp);
            genIFNotNullJump(temp, labelTrue);
            genGotoLabel(labelFalse);
            break;
    }
    int temp1 = newTemp();
    int temp2 = newTemp();

}

void translateStmt(Node* stmt) {
    switch (stmt->cnt) {
        case 2 :
            translateExp(stmt->ch[0], -1);
            break;
        case 1 :
            translateCompSt(stmt->ch[0]);
            break;
        case 3 :
            int temp = getTemp();
            translateExp(stmt->ch[1], temp);
            genFunctionReturn(temp);
            break;
        case 5 :
            if (stmt->ch[0]->elem == SybIF) {
                int labelTrue = newLabel(); // condition is true
                int labelFalse = newLabel(); // condition is false
                translateCondition(stmt->ch[2], labelTrue, labelFalse);
                genLabel(labelTrue);
                translateStmt(stmt->ch[4]);
                genLabel(labelFalse);
            } else if (stmt->ch[0]->elem == SybWHILE) {
                int labelStart = newLabel(); // at begin of while
                int labelTrue = newLabel(); // condition is true
                int labelFalse = newLabel(); // condition is false
                genLabel(labelStart);
                translateCondition(stmt->ch[2], labelTrue, labelFalse);
                genLabel(labelTrue);
                translateStmt(stmt->ch[4]);
                genGotoLabel(labelStart);
                genLabel(labelFalse);
            }
            break;
        case 7 :
            int labelTrue = newLabel(); // condition is true
            int labelFalse = newLabel(); // condition is false 
            int labelEnd = newLabel(); // the end of if-else
            translateCondition(stmt->ch[2], labelTrue, labelFalse);
            genLabel(labelTrue);
            translateStmt(stmt->ch[4]);
            genGotoLabel(labelEnd);
            genLabel(labelFalse);
            translateStmt(stmt->ch[6]);
            genLabel(labelEnd);
            break;
        default :
            break;
    }
}

void translateDefList(Node* deflist) {
    if (deflist == NULL) return ;
    translateDef(deflist->ch[0]);
    translateDefList(deflist->ch[1]);
}

void translateDef(Node* def) {
    translateSpecifier(def->ch[0]);
    translateDecList(def->ch[1]);
}

void translateDecList(Node* decList) {
    // printf("DecList\n");
    translateDec(decList->ch[0]);
    if (decList->cnt > 1) {
        translateDecList(decList->ch[2]);
    }
}

void translateDec(Node* dec) {
    // printf("Dec\n");
    translateVarDec(dec->ch[0]);
    if (dec->cnt > 1) { // define and initial
        int temp = getTemp();
        translateExp(dec->ch[2], temp);
        int var = getVarNo(dec->ch[0]->snode->name);
        genVariableAssign(var, temp);
    }
}

int isArray(char* name) {
    if (name == NULL) return 0;
    return isSymbolArray(name);
}

// fill value of every var generate
void translateExp(Node* exp, int place) {
    switch (exp->cnt) {
        case 3 :
            if (exp->ch[1]->elem == SybASSIGNOP) {
                if (!isArray(exp->ch[0]->snode->name)) {
                    int var = getVarNo(exp->snode->name);
                    translateExp(exp->ch[2], var);
                } else if (isArray(exp->ch[0]->snode->name)) {
                    int address = getTemp();
                    translateExp(exp->ch[0], address); // ADDRESS TYPE
                    if (!isArray(exp->ch[2]->snode->name)) {
                        translateExp(exp->ch[2], address);
                    } else if (isArray(exp->ch[2]->snode->name)) {
                        int address2 = getTemp();
                        translateExp(exp->ch[2], address2);
                        genVariableAssign(address, address2);
                    }
                }

            } else if (
                exp->ch[1]->elem == SybPLUS ||
                exp->ch[1]->elem == SybMINUS ||
                exp->ch[1]->elem == SybSTAR ||
                exp->ch[1]->elem == SybDIV
            ) {
                int temp1 = getTemp();
                int temp2 = getTemp();
                translateExp(exp->ch[0], temp1);
                translateExp(exp->ch[2], temp2);
                if(place != -1) genVariableOperation(place, temp1, temp2, exp->ch[1]->elem);
            } else if (
                exp->ch[1]->elem == SybRELOP ||
                exp->ch[1]->elem == SybAND ||
                exp->ch[1]->elem == SybOR
            ) {
            } else if (exp->ch[0]->elem == SybID) {
                genCallFunction(place, exp->snode->name);
            } else if (exp->ch[1]->elem == SybExp) {
                translateExp(exp->ch[1], place);
            }
            break;
        case 2 :
            if (exp->ch[0]->elem == SybMINUS) {
                int temp = getTemp();
                translateExp(exp->ch[1], temp);
                if(place != -1) genMinusOperation(place, temp);
            }
            break;
        case 4 :
            if (exp->ch[2]->elem == SybArgs) {
                if (!strcmp(exp->snode->name, "write")) {
                    int temp = getTemp(); // for write function
                    translateArgs(exp->ch[2], temp, 0);
                    genCallFunction(temp, exp->snode->name);
                } else {
                    translateArgs(exp->ch[2], getTemp(), 1);
                    genCallFunction(place, exp->snode->name);
                }
            } else if(exp->ch[2]->elem == SybExp) {
                int arrno = getVarNo(exp->snode->name);
                int index = getTemp();
                translateExp(exp->ch[2], index);
                int offset = getTemp();
                if(place != -1) genArrayAddress(place, arrno, index, offset);
            }
            break;
        case 1 :
            if (exp->ch[0]->elem == SybID) {
                // getVarNo(exp->snode->name);
                if(place != -1) genVariableAssign(place, getVarNo(exp->snode->name));
            } else if (exp->ch[0]->elem == SybINT) {
                if(place != -1) genConstantAssign(place, exp->snode->type->constant.intval);
            } 
        default :
            break;
    }
    
}

void translateArgs(Node* args, int place, int flag) {
    translateExp(args->ch[0], place);
    if (args->cnt > 1) {
        int temp = getTemp();
        translateArgs(args->ch[2], temp, flag);
    }
    if (flag) genFunctionArgument(place);
}


int getTemp() {
    return newTemp();
}

int getVarNo(char* name) {
    int id = getSymbolIndex(name);
    return symbolno[id] ? symbolno[id] : (symbolno[id] = getTemp());
}

void showCodes(char* out) {
    printCodes(out);
}